package com.unitd.base.frame.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.nio.file.FileSystemException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 文件处理工具类
 *
 * @author Hudan
 * @version 1.0
 * @filename FileUtils.java
 * @copyright www.unitd.com
 * @date 2019-01-23
 */
@Slf4j
public class FileUtils extends org.apache.commons.io.FileUtils {

    /**
     * 获取指定文件夹下的指定后缀文件列表
     *
     * @param results    结果文件列表
     * @param directory  指定的需要筛查的文件夹
     * @param extensions 文件后缀
     * @return List
     */
    public static List<File> listFiles(List<File> results, File directory, final String extensions) {

        File[] subFiles = directory.listFiles();
        for (File file : subFiles) {
            if (file.isDirectory()) {
                listFiles(results, file, extensions);
            } else if (file.getName().endsWith(extensions)) {
                results.add(file);
            }
        }
        return results;
    }

    /**
     * 获取jar文件中文件列表
     *
     * @param jarFile    指定的jar文件
     * @param extensions 文件后缀
     * @return List
     */
    public static List<String> listFiles(JarFile jarFile, String extensions) {
        if (jarFile == null || StringUtils.isEmpty(extensions))
            return null;

        List<String> files = new ArrayList<>();

        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            String name = entry.getName();

            if (name.endsWith(extensions)) {
                files.add(name);
            }
        }
        return files;
    }

    /**
     * 获取项目的相对路径下文件的绝对路径
     *
     * @param parentDir 目标文件的父目录
     *                  (例如说:工程的目录下,有lib与bin和conf目录,那么程序运行于lib与bin,那么需要的配置文件却是conf里面,则需要找到该配置文件的绝对路径)
     * @param fileName  文件名
     * @return 一个绝对路径
     */
    public static String getPath(String parentDir, String fileName) {

        String path;
        String userdir = System.getProperty("user.dir");
        String userdirName = new File(userdir).getName();

        if (userdirName.equalsIgnoreCase("lib")
                || userdirName.equalsIgnoreCase("bin")) {

            File newf = new File(userdir);
            File newp = new File(newf.getParent());

            if (fileName.trim().equals("")) {
                path = newp.getPath() + File.separator + parentDir;
            } else {
                path = newp.getPath() + File.separator + parentDir + File.separator + fileName;
            }
        } else {
            if (fileName.trim().equals("")) {
                path = userdir + File.separator + parentDir;
            } else {
                path = userdir + File.separator + parentDir + File.separator + fileName;
            }
        }
        return path;
    }

    /**
     * 获取文件后缀名(不带点)
     *
     * @param fileName 文件名
     * @return String
     */
    public static String getFileExt(String fileName) {
        if (StringUtils.isBlank(fileName) || !fileName.contains(".")) {
            return "";
        } else {
            return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); // 不带最后的点
        }
    }

    /**
     * 压缩文件(防止中文乱码,使用apache的ZipOutputStream包)
     *
     * @param strs     需要压缩的文件数组
     * @param zipname  压缩文件名称
     * @param temppath 临时目录
     */
    private static void writeZip(String[] strs, String zipname, String temppath) throws IOException {
        String[] files = strs;
        OutputStream os = new BufferedOutputStream(new FileOutputStream(temppath + "//" + zipname));
        ZipOutputStream zos = new ZipOutputStream(os);
        byte[] buf = new byte[8192];
        int len;
        for (int i = 0; i < files.length; i++) {
            File file = new File(files[i]);
            if (!file.isFile()) {
                continue;
            }

            ZipEntry ze = new ZipEntry(file.getName());
            zos.putNextEntry(ze);
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
            while ((len = bis.read(buf)) > 0) {
                zos.write(buf, 0, len);
            }
            zos.closeEntry();
            bis.close();
        }
        for (int i = 0; i < files.length; i++) {
            File file = new File(files[i]);
            file.delete();
        }
        zos.closeEntry();
        zos.close();
        os.close();
    }

    /**
     * 删除单个文件
     *
     * @param fileName 被删除的文件名
     * @return 如果删除成功，则返回true，否则返回false
     */
    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        if (file.exists() && file.isFile()) {
            if (file.delete()) {
                log.debug("删除文件 " + fileName + " 成功!");
                return true;
            } else {
                log.debug("删除文件 " + fileName + " 失败!");
                return false;
            }
        } else {
            log.debug(fileName + " 文件不存在!");
            return true;
        }
    }

    /**
     * 删除目录及目录下的文件
     *
     * @param dirName 被删除的目录所在的文件路径
     * @return 如果目录删除成功，则返回true，否则返回false
     */
    public static boolean deleteDirectory(String dirName) {
        String dirNames = dirName;
        if (!dirNames.endsWith(File.separator)) {
            dirNames = dirNames + File.separator;
        }
        File dirFile = new File(dirNames);
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            log.debug(dirNames + " 目录不存在!");
            return true;
        }

        boolean flag = true;
        // 列出全部文件及子目录
        File[] files = dirFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            // 删除子文件
            if (files[i].isFile()) {
                flag = FileUtils.deleteFile(files[i].getAbsolutePath());
                // 如果删除文件失败，则退出循环
                if (!flag) {
                    break;
                }
            }
            // 删除子目录
            else if (files[i].isDirectory()) {
                flag = FileUtils.deleteDirectory(files[i]
                        .getAbsolutePath());
                // 如果删除子目录失败，则退出循环
                if (!flag) {
                    break;
                }
            }
        }

        if (!flag) {
            log.debug("删除目录失败!");
            return false;
        }
        // 删除当前目录
        if (dirFile.delete()) {
            log.debug("删除目录 " + dirName + " 成功!");
            return true;
        } else {
            log.debug("删除目录 " + dirName + " 失败!");
            return false;
        }

    }

    /**
     * 创建单个文件
     *
     * @param descFileName 文件名，包含路径
     * @return 如果创建成功，则返回true，否则返回false
     */
    public static boolean createFile(String descFileName) {
        File file = new File(descFileName);
        if (file.exists()) {
            log.debug("文件 " + descFileName + " 已存在!");
            return false;
        }
        if (descFileName.endsWith(File.separator)) {
            log.debug(descFileName + " 为目录，不能创建目录!");
            return false;
        }
        if (!file.getParentFile().exists()) {
            // 如果文件所在的目录不存在，则创建目录
            if (!file.getParentFile().mkdirs()) {
                log.debug("创建文件所在的目录失败!");
                return false;
            }
        }

        // 创建文件
        try {
            if (file.createNewFile()) {
                log.debug(descFileName + " 文件创建成功!");
                return true;
            } else {
                log.debug(descFileName + " 文件创建失败!");
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.debug(descFileName + " 文件创建失败!");
            return false;
        }
    }

    /**
     * 创建目录
     *
     * @param descDirName 目录名,包含路径
     * @return 如果创建成功，则返回true，否则返回false
     */
    public static boolean createDirectory(String descDirName) {
        String descDirNames = descDirName;
        if (!descDirNames.endsWith(File.separator)) {
            descDirNames = descDirNames + File.separator;
        }
        File descDir = new File(descDirNames);
        if (descDir.exists()) {
            log.debug("目录 " + descDirNames + " 已存在!");
            return false;
        }
        // 创建目录
        if (descDir.mkdirs()) {
            log.debug("目录 " + descDirNames + " 创建成功!");
            return true;
        } else {
            log.debug("目录 " + descDirNames + " 创建失败!");
            return false;
        }

    }

    /**
     * 写入文件
     *
     * @param fileName 要写入的文件名称
     * @param content  要写入文件的内容
     * @param append   是否往文件默认写入
     */
    public static void writeToFile(String fileName, String content, boolean append) {
        writeToFile(fileName, content, "UTF-8", append);
    }

    /**
     * 写入文件
     *
     * @param fileName 要写入的文件名称
     * @param content  要写入文件的内容
     * @param encoding 编码格式
     * @param append   是否往文件默认写入
     */
    public static void writeToFile(String fileName, String content, String encoding, boolean append) {
        try {
            write(new File(fileName), content, encoding, append);
            log.debug("文件 " + fileName + " 写入成功!");
        } catch (IOException e) {
            log.debug("文件 " + fileName + " 写入失败! " + e.getMessage());
        }
    }


    /**
     * 获取文件分隔符
     *
     * @return
     */
    public static String getFileSplit() {
        return System.getProperty("file.separator");
    }

    public static void copyFile(File source, File dest)
            throws FileSystemException {
        try {
            if (!source.exists()) {
                throw new FileSystemException("源文件不存在");
            }
            if (!source.isFile()) {
                throw new FileSystemException("源文件是目录");
            }

            if (dest.exists()) {
                dest.delete();
            }
            if (!dest.createNewFile()) {
                throw new FileSystemException("不能建立文件:" + dest);
            }
        } catch (Exception e) {
            throw new FileSystemException(e.getMessage());
        }
        try {
            FileInputStream sourceStream = new FileInputStream(source);
            FileOutputStream destStream = new FileOutputStream(dest);
            byte[] buf = new byte[1024];
            int len = 0;
            len = sourceStream.read(buf);
            while (len > 0) {
                destStream.write(buf, 0, len);
                len = sourceStream.read(buf);
            }
            sourceStream.close();
            destStream.flush();
            destStream.close();
        } catch (Exception e) {
            throw new FileSystemException(e.getMessage());
        }
    }

    /**
     * 创建对应的目录
     *
     * @param path 文件路径
     * @return boolean
     */
    public static boolean createPath(File path) {
        if (!path.exists()) {
            return path.mkdir();
        }
        return true;
    }

    /**
     * Springboot项目中获取当前web的根目录
     *
     * @return web应用根目录
     */
    public static String getJarRootPath() {
        try {
            String path = ResourceUtils.getURL("classpath:").getPath();
            //=> file:/root/tmp/demo-springboot-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
            log.debug("ResourceUtils.getURL(\"classpath:\").getPath() -> " + path);

            //创建File时会自动处理前缀和jar包路径问题  => /root/tmp
            File rootFile = new File(path);
            if (!rootFile.exists()) {
                log.info("根目录不存在, 重新创建");
                rootFile = new File("");
                log.info("重新创建的根目录: " + rootFile.getAbsolutePath());
            }
            // 获取的字符串末尾没有分隔符 /
            log.debug("项目根目录: " + rootFile.getAbsolutePath());
            return rootFile.getAbsolutePath();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}